/**
 * @file present.cpp
 * @brief  PRESENT算法的C++实现
 * @author GuoJi
 * @version 1.0
 * @date 2023-06-02
 * 
 * @copyright Copyright (c) 2023  code from HUIYU
 */
#include <bits/stdc++.h>
#include <windows.h>   

//宏开关 是否输出详细过程
#define detial 1
//改动这里选择明文和密钥
#define PLAIN  0x0
#define KEY1   0xFFFFFFFFFF 
#define KEY2   0xFFFFFFFFFF 



using namespace std;

clock_t startTime,endTime;

//加密的Sbox以及解密的inv_sbox
bitset<4> s_box[16]= {0xC,0x5,0x6,0xB,0x9,0x0,0xA,0xD,0x3,0xE,0xF,0x8,0x4,0x7,0x1,0x2};
bitset<4> inv_sbox[16]= {0x5,0xE,0xF,0x8,0xC,0x1,0x2,0xD,0xB,0x4,0x6,0x3,0x0,0x7,0x9,0xA};


/**
 * @brief  进行字节代换
 * @author GuoJi
 * @param  state     输入的明文通过此函数进行字节代换   
 */
void SubByte(bitset<64> &state)
{
    for(int i=0; i<64; i+=4)
    {
        bitset<4> s=s_box[state[i]+(state[i+1]<<1)+(state[i+2]<<2)+(state[i+3]<<3)];
        state[i]=s[0];
        state[i+1]=s[1];
        state[i+2]=s[2];
        state[i+3]=s[3];
    }
}


/**
 * @brief  字节逆代换   
 * @author GuoJi
 * @param  state           输入的明文通过此函数进行字节逆代换   
 */
void InvSubByte(bitset<64> &state)
{
    for(int i=0; i<64; i+=4)
    {
        bitset<4> s=inv_sbox[state[i]+(state[i+1]<<1)+(state[i+2]<<2)+(state[i+3]<<3)];
        state[i]=s[0];
        state[i+1]=s[1];
        state[i+2]=s[2];
        state[i+3]=s[3];
    }
}

/**
 * @brief  P置换，这里直接使用公式
 * @author GuoJi
 * @param  state            
 */
void PSub(bitset<64> &state)
{
    bitset<64> tmp(0);
    for(int i=0; i<63; i++)
        tmp[i*16%63]=state[i];
    tmp[63]=state[63];
    state=tmp;
}

/**
 * @brief  P置换解密，这里直接使用公式
 * @author GuoJi
 * @param  state           
 */
void InvPSub(bitset<64> &state)
{
    bitset<64> tmp(0);
    for(int i=0; i<63; i++)
        tmp[i]=state[i*16%63];
    tmp[63]=state[63];
    state=tmp;
}

/**
 * @brief 密钥拓展
 * @author GuoJi
 * @param  key              密钥
 * @param  rc               轮数
 */
void keyUpdate(bitset<80> &key,bitset<5> rc)
{
    key = (key<<61)|(key>>19);   //循环右移
    bitset<4> s=s_box[(key[79]<<3)+(key[78]<<2)+(key[77]<<1)+key[76]];
    key[79]=s[3];
    key[78]=s[2];
    key[77]=s[1];
    key[76]=s[0];
    key[19]=key[19]^rc[4];
    key[18]=key[18]^rc[3];
    key[17]=key[17]^rc[2];
    key[16]=key[16]^rc[1];
    key[15]=key[15]^rc[0];
}

/**
 * @brief  密钥轮加
 * @author GuoJi
 * @param  state           
 */
void addRoundKey(bitset<64> &state,const bitset<80>key)
{
    for(int i=0; i<64; i++)
        state[63-i]=state[63-i]^key[79-i];
}

/**
 * @brief 加密算法
 * @author GuoJi
 * @param  state             明文
 * @param  key               密钥
 * @return bitset<64>       
 */
bitset<64> encrypt(bitset<64> state,bitset<80> key)
{
    for(int RC=1; RC<32; RC++)
    {
        addRoundKey(state,key);
        SubByte(state);
        PSub(state);
        keyUpdate(key,RC);
#if detial
        printf("round %d state is ",RC);
        for(int i=15;i>=0;i--)
        {
          printf("%x",((state[i*4+3]<<3)+(state[i*4+2]<<2)+(state[i*4+1]<<1)+state[i*4])); 
        }
         cout<<endl;
         printf("round %d key is ",RC);
        for(int i=19;i>=0;i--)
        {
          printf("%x",((key[i*4+3]<<3)+(key[i*4+2]<<2)+(key[i*4+1]<<1)+key[i*4])); 
        }
        cout<<endl;
#endif
    }
    addRoundKey(state,key);
    return state;
}

/**
 * @brief 解密算法
 * @author GuoJi
 * @param  state             明文
 * @param  key               密钥
 * @return bitset<64>       
 */
bitset<64> decrypt(bitset<64> state,bitset<80> key)
{
    vector<bitset<80> >k;
    k.push_back(key);
    for(int i=1;i<32;i++)
    {
        keyUpdate(key,i);
        k.push_back(key);
    }
    for(int i=31; i>=1; i--)
    {
        addRoundKey(state,k[i]);
        InvPSub(state);
        InvSubByte(state);
    }
    addRoundKey(state,k[0]);
    return state;
}
int main()
{
    double time;

    bitset<64> plain = PLAIN;
    bitset<80> key;
    bitset<40> key1=KEY1,key2=KEY2;
    for(int m=0;m<80;m++)
    {
       if(m<40)
       key[m]=key1[m];
       else
       key[m]=key2[m-40];
    }
    //时间计算
    LARGE_INTEGER t1,t2,tc;
    QueryPerformanceFrequency(&tc);
    QueryPerformanceCounter(&t1);
    bitset<64> cipher = encrypt(plain,key);
    QueryPerformanceCounter(&t2);
    time=(double)(t2.QuadPart-t1.QuadPart)/(double)tc.QuadPart; 
    cout<<"the Algorithm running time = "<<time*1000000<<"us"<<endl;


    cout<<"the Binary ciphertext: "<<cipher<<endl;
    cout<<"the Binary plaintext: "<<decrypt(cipher,key)<<endl;
    printf("round 32 and the final is ");
     for(int i=15;i>=0;i--)
        printf("%x",((cipher[i*4+3]<<3)+(cipher[i*4+2]<<2)+(cipher[i*4+1]<<1)+cipher[i*4]));
    cout<<endl;   

   system("pause"); 
   return 0;
}



